/* poppler-ps-converter.cc: qt interface to poppler
 * Copyright (C) 2007, 2009, 2010, 2015, 2020, Albert Astals Cid <aacid@kde.org>
 * Copyright (C) 2008, Pino Toscano <pino@kde.org>
 * Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
 * Copyright (C) 2011 Glad Deschrijver <glad.deschrijver@gmail.com>
 * Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
 * Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
 * Copyright (C) 2014 Adrian Johnson <ajohnson@redneon.com>
 * Copyright (C) 2020 William Bader <williambader@hotmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "poppler-qt5.h"

#include "poppler-private.h"
#include "poppler-converter-private.h"

#include "PSOutputDev.h"

static void outputToQIODevice(void *stream, const char *data, int len)
{
    static_cast<QIODevice *>(stream)->write(data, len);
}

namespace Poppler {

class PSConverterPrivate : public BaseConverterPrivate
{
public:
    PSConverterPrivate();
    ~PSConverterPrivate() override;

    QList<int> pageList;
    QString title;
    double hDPI;
    double vDPI;
    int rotate;
    int paperWidth;
    int paperHeight;
    int marginRight;
    int marginBottom;
    int marginLeft;
    int marginTop;
    PSConverter::PSOptions opts;
    void (*pageConvertedCallback)(int page, void *payload);
    void *pageConvertedPayload;
};

PSConverterPrivate::PSConverterPrivate()
    : BaseConverterPrivate(),
      hDPI(72),
      vDPI(72),
      rotate(0),
      paperWidth(-1),
      paperHeight(-1),
      marginRight(0),
      marginBottom(0),
      marginLeft(0),
      marginTop(0),
      opts(PSConverter::Printing),
      pageConvertedCallback(nullptr),
      pageConvertedPayload(nullptr)
{
}

PSConverterPrivate::~PSConverterPrivate() = default;

PSConverter::PSConverter(DocumentData *document) : BaseConverter(*new PSConverterPrivate())
{
    Q_D(PSConverter);
    d->document = document;
}

PSConverter::~PSConverter() { }

void PSConverter::setPageList(const QList<int> &pageList)
{
    Q_D(PSConverter);
    d->pageList = pageList;
}

void PSConverter::setTitle(const QString &title)
{
    Q_D(PSConverter);
    d->title = title;
}

void PSConverter::setHDPI(double hDPI)
{
    Q_D(PSConverter);
    d->hDPI = hDPI;
}

void PSConverter::setVDPI(double vDPI)
{
    Q_D(PSConverter);
    d->vDPI = vDPI;
}

void PSConverter::setRotate(int rotate)
{
    Q_D(PSConverter);
    d->rotate = rotate;
}

void PSConverter::setPaperWidth(int paperWidth)
{
    Q_D(PSConverter);
    d->paperWidth = paperWidth;
}

void PSConverter::setPaperHeight(int paperHeight)
{
    Q_D(PSConverter);
    d->paperHeight = paperHeight;
}

void PSConverter::setRightMargin(int marginRight)
{
    Q_D(PSConverter);
    d->marginRight = marginRight;
}

void PSConverter::setBottomMargin(int marginBottom)
{
    Q_D(PSConverter);
    d->marginBottom = marginBottom;
}

void PSConverter::setLeftMargin(int marginLeft)
{
    Q_D(PSConverter);
    d->marginLeft = marginLeft;
}

void PSConverter::setTopMargin(int marginTop)
{
    Q_D(PSConverter);
    d->marginTop = marginTop;
}

void PSConverter::setStrictMargins(bool strictMargins)
{
    Q_D(PSConverter);
    if (strictMargins)
        d->opts |= StrictMargins;
    else
        d->opts &= ~StrictMargins;
}

void PSConverter::setForceRasterize(bool forceRasterize)
{
    Q_D(PSConverter);
    if (forceRasterize)
        d->opts |= ForceRasterization;
    else
        d->opts &= ~ForceRasterization;
}

void PSConverter::setPSOptions(PSConverter::PSOptions options)
{
    Q_D(PSConverter);
    d->opts = options;
}

PSConverter::PSOptions PSConverter::psOptions() const
{
    Q_D(const PSConverter);
    return d->opts;
}

void PSConverter::setPageConvertedCallback(void (*callback)(int page, void *payload), void *payload)
{
    Q_D(PSConverter);
    d->pageConvertedCallback = callback;
    d->pageConvertedPayload = payload;
}

static bool annotDisplayDecideCbk(Annot *annot, void *user_data)
{
    if (annot->getType() == Annot::typeWidget)
        return true; // Never hide forms
    else
        return *(bool *)user_data;
}

bool PSConverter::convert()
{
    Q_D(PSConverter);
    d->lastError = NoError;

    Q_ASSERT(!d->pageList.isEmpty());
    Q_ASSERT(d->paperWidth != -1);
    Q_ASSERT(d->paperHeight != -1);

    if (d->document->locked) {
        d->lastError = FileLockedError;
        return false;
    }

    QIODevice *dev = d->openDevice();
    if (!dev) {
        d->lastError = OpenOutputError;
        return false;
    }

    QByteArray pstitle8Bit = d->title.toLocal8Bit();
    char *pstitlechar;
    if (!d->title.isEmpty())
        pstitlechar = pstitle8Bit.data();
    else
        pstitlechar = nullptr;

    std::vector<int> pages;
    foreach (int page, d->pageList) {
        pages.push_back(page);
    }

    PSOutputDev *psOut = new PSOutputDev(outputToQIODevice, dev, pstitlechar, d->document->doc, pages, (d->opts & PrintToEPS) ? psModeEPS : psModePS, d->paperWidth, d->paperHeight, false, false, d->marginLeft, d->marginBottom,
                                         d->paperWidth - d->marginRight, d->paperHeight - d->marginTop, (d->opts & ForceRasterization) ? psAlwaysRasterize : psRasterizeWhenNeeded);

    if (d->opts & StrictMargins) {
        double xScale = ((double)d->paperWidth - (double)d->marginLeft - (double)d->marginRight) / (double)d->paperWidth;
        double yScale = ((double)d->paperHeight - (double)d->marginBottom - (double)d->marginTop) / (double)d->paperHeight;
        psOut->setScale(xScale, yScale);
    }

    if (psOut->isOk()) {
        bool isPrinting = (d->opts & Printing) ? true : false;
        bool showAnnotations = (d->opts & HideAnnotations) ? false : true;
        foreach (int page, d->pageList) {
            d->document->doc->displayPage(psOut, page, d->hDPI, d->vDPI, d->rotate, false, true, isPrinting, nullptr, nullptr, annotDisplayDecideCbk, &showAnnotations, true);
            if (d->pageConvertedCallback)
                (*d->pageConvertedCallback)(page, d->pageConvertedPayload);
        }
        delete psOut;
        d->closeDevice();
        return true;
    } else {
        delete psOut;
        d->closeDevice();
        return false;
    }
}

}
